package tests;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import peerToPeerFacebook.proj.INode;
import peerToPeerFacebook.proj.RPCBase;
import peerToPeerFacebook.src.edu.washington.cs.cse490h.lib.Callback;
import peerToPeerFacebook.src.edu.washington.cs.cse490h.lib.Packet;

public class TestNode implements INode
{
	protected class Message
	{
		int from;
		int protocol;
		byte[] payload;
		
		public Message (int from, int protocol, byte[] payload)
		{
			this.from = from;
			this.protocol = protocol;
			this.payload = payload;
		}
	}
	
	public ArrayList<Message> pendingMessages = new ArrayList<Message>();
	public Map<String, ByteArrayOutputStream> files = new HashMap<String, ByteArrayOutputStream>();
	public ArrayList<Callback> callbacks = new ArrayList<Callback> ();
	public int maxPacketSize = Packet.MAX_PAYLOAD_SIZE;

	protected TestNode peerTestNode = null;
	protected RPCBase rpcBase = null;
	
	public TestNode()
	{ }
	
	public void setTargets(TestNode peer, RPCBase target)
	{
		this.peerTestNode = peer;
		this.rpcBase = target;
	}
		
	public void timeout() throws IllegalAccessException, InvocationTargetException
	{
		// callbacks are one shot approach, thus clear them
		ArrayList<Callback> oldCallbacks = callbacks;
		callbacks = new ArrayList<Callback>();
		
		for (Callback callback: oldCallbacks)
			callback.invoke(); 
	}
	
	public void deliverPacket()
	{
		Message msg = pendingMessages.get(0);
		pendingMessages.remove(0);
		rpcBase.onReceive(msg.from, msg.protocol, msg.payload);
	}
	
	public void deliverAllPackets()
	{
		// Not the most efficient, but easy to implement
		while (pendingMessages.size() != 0)
			deliverPacket();
	}
	
	public void dropPacket()
	{
		pendingMessages.remove(0);	// Opps
	}
	
	public void dropAllpackets()
	{
		pendingMessages.clear();
	}
	
	public void addMessages(TestNode other)
	{
		pendingMessages.addAll(other.pendingMessages);
	}
	
	public void swapPackets(int i, int j)
	{
		Message temp = pendingMessages.get(i);
		pendingMessages.set(i, pendingMessages.get(j));
		pendingMessages.set(j, temp);
	}
	
	@Override
	public void send(int destAddr, int protocol, byte[] payload)
	{
		// Well packet needs to know where it come from so we store that one instead of destination address.
		peerTestNode.pendingMessages.add(new Message(getAddr(), protocol, payload));
	}

	@Override
	public void addTimeout(Callback cb, int timer)
	{
		this.callbacks.add(cb);
	}

	@Override
	public InputStream getInputStream(String filename)
			throws FileNotFoundException
	{
		// See if the file exists
		if (!files.containsKey(filename))
			throw new FileNotFoundException();
		
		ByteArrayOutputStream stream = files.get(filename);
		return new ByteArrayInputStream(stream.toByteArray());
	}

	@Override
	public OutputStream getOutputStream(String filename,
			boolean append) throws IOException
	{
		if (!append)
			files.put(filename, new ByteArrayOutputStream());
		
		if (files.containsKey(filename))
			return files.get(filename);
		else
			throw new FileNotFoundException();		
	}

	@Override
	public void logSynopticEvent(String eventStr)
	{
		System.out.println(eventStr);
	}

	@Override
	public int getAddr()
	{
		return this.hashCode();
	}

	@Override
	public int maxPacketSize()
	{
		return maxPacketSize;
	}
}
